#!coding:utf-8
from libvirt import libvirtError
from cvirt.cnode.libvirtXmlObject import NetworkFilter, FilterRules, Rule
from cvirt.cnode.connection import get_connection
import logging
from cvirt.common import config
from cvirt.cnode.network import bridge
LOG = logging.getLogger("nwfilter.api")

conf = config.load_settings()

def refresh_secruity_group(sgroup):
    filter = NetworkFilter()
    filter.name = sgroup.name
    filter.chain = 'ipv4'
    LOG.debug('apply rules in group %s ' % sgroup.name)
    for rule in sgroup.securitygroupingressrule_set.all():
        robject = Rule(protocal=rule.protocol)
        #相对vm来说，in或者out我们只需要限制目标地址和端口就可以了
        if rule.dstipfrom:
            robject.dstipfrom = rule.dstipfrom
        if rule.dstipto:
            robject.dstipto = rule.dstipto
        if rule.dstportstart:
            robject.dstportstart = rule.dstportstart
        if rule.dstportend:
            robject.dstportend = rule.dstportend

        #inout是双向限制
        if rule.direction == 'inout':
            if rule.srcipfrom:
                robject.srcipfrom = rule.srcipfrom
            if rule.srcipto:
                robject.srcipto = rule.srcipto
            if rule.srcportstart:
                robject.srcportstart = rule.srcportstart
            if rule.srcportend:
                robject.srcportend = rule.srcportend


        chain = FilterRules(action=rule.action,direction=rule.direction,priority=rule.priority,
                            ip_rules=[robject])
        filter.rules.append(chain)
    LOG.info("generate xml for securitygroup %s:\n%s" % (sgroup.name,filter.to_xml()))
    #libvirt api会覆盖已经存在的
    get_connection().nwfilterDefineXML(filter.to_xml())

    # #刷新安全组下的虚拟机网卡
    # for inst in sgroup.interface_set.filter(enable=True):
    #     refresh_filter_for_interface(inst)


def refresh_filter_for_interface(interface):
    filter = NetworkFilter()
    filter.chain = 'ipv4'
    filter.name = "nwfilter-%s-%s" % (interface.instance.name, interface.name)
    #TODO clean-traffic和allow-dhcp-server在所有规则的顶部
    #TODO libvirt内置的clean-traffic包含我们不需要的allow-incoming-ipv4规则
    # filter.refs.append("clean-traffic")

    dhcpserver = None
    if interface.network and interface.network.rundhcp:
        if interface.network.networktype in [0,1]:#nat和isolate
            dhcpserver = interface.network.address
        else:
            networkip = bridge.allocate_ip_for_network(interface.network)
            dhcpserver = networkip.address

    #当前版本的libvirt并不支持libvirt内置的规则组中的ip标签，所以不能直接使用allow-dhcp-server和allow-dhcp
    dhcpout = FilterRules(action='accept',direction='out',priority="100",
                ip_rules=[Rule(protocal='udp',srcipaddr='0.0.0.0',dstipaddr='255.255.255.255',srcportstart='68',dstportstart='67')])
    dhcpin = FilterRules(action='accept',direction='in',priority="100",
                ip_rules=[Rule(protocal='udp',srcipaddr=dhcpserver,srcportstart='67',dstportstart='68')])

    filter.rules.append(dhcpout)
    filter.rules.append(dhcpin)

    for sgroup in interface.securitygroups.all():
        #如果不存在则创建
        if not look_up_by_name(sgroup.name):
            try:
                refresh_secruity_group(sgroup)
            except:
                LOG.warn('refresh secruity group %s failed' % sgroup.name)
        filter.refs.append(sgroup.name)

    if conf.get_bool('firewall.default'):
        #默认禁止访问在所有规则的底部
        chain = FilterRules(action='drop',direction='inout',priority="1000",
                                ip_rules=[Rule(protocal='all')])
        filter.rules.append(chain)

    LOG.info("generate xml for interface %s:\n%s" % (interface.name,filter.to_xml()))
    #libvirt api会覆盖已经存在的
    get_connection().nwfilterDefineXML(filter.to_xml())


def refresh_filter_for_instance(instance):
    for interface in instance.interface_set.filter(enable=True):
        refresh_filter_for_interface(interface)

def look_up_by_name(name):
    try:
        return get_connection().nwfilterLookupByName(name)
    except libvirtError:
        pass

def remove_nwfilter(name):
    try:
        nwdomain = look_up_by_name(name)
        if nwdomain:
            nwdomain.undefine()
            return nwdomain
    except libvirtError as e:
        if not 'nwfilter is in use' in str(e):
            raise


def remove_instance_filter(instance):
    for interface in instance.interface_set.filter(enable=True):
        remove_interface_filter(interface)

def remove_interface_filter(interface):
    remove_nwfilter("nwfilter-%s-%s" % (interface.instance.name, interface.name))
